//------------------------------------------------------------------------------
//SFWK Framework
//Copyright (C) 2011 SQLI

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program.  If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------


using System;
using System.Reflection;
using System.Linq;
using System.Collections;
using SFWK.Core.DTO;

namespace SFWK.Core
{
    /// <summary>
    /// Provides methods to manage entities
    /// </summary>
    public class EntityHelper
    {
        public static char DecimalSeparator = Convert.ToChar(System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator);

        /// <summary>
        /// Clones an entity
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static T Clone<T>(T source)
        {
            var dcs = new System.Runtime.Serialization.DataContractSerializer(typeof(T));
            using (var ms = new System.IO.MemoryStream())
            {
                dcs.WriteObject(ms, source);
                ms.Seek(0, System.IO.SeekOrigin.Begin);
                return (T)dcs.ReadObject(ms);
            }
        }




        #region Checking Null Values
        /// <summary>
        /// Checks if an entity is null or empty
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsNullOrEmpty<T>(T value)
        {
            if (IsNull(value)) { return true; }
            if ((value is Guid) && (value.ToString() == Guid.Empty.ToString())) { return true; }
            if (value is string) { return string.IsNullOrEmpty(value as string); }
            return value.Equals(default(T));
        }

        /// <summary>
        /// return empty if valu is null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string NullStringToEntry(string value)
        {
            if (IsNull(value)) { return string.Empty; }
            return value;
        }

        /// <summary>
        /// Checks if an entity is null
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsNull<T>(T value)
        {
            if (value is ValueType) { return false; }
            return null == (object)value;
        }



        /// <summary>
        /// Get the value of a string : remove insignificant Zero
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static decimal GetValue(string value)
        {
            decimal val = 0;
            decimal.TryParse(value, out val);
            //#824 : [RO ADD] Crach Lors de la saisie d'un nombre assez long dans le champs "RO Quantity" 
            if (val > 0 && val != decimal.Truncate(val))
                return decimal.Parse(val.ToString().TrimEnd(DecimalSeparator, '0'));
            else
                return (decimal.Truncate(val));

        }
        /// <summary>
        /// Get the value of a string : remove insignificant Zero
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static decimal GetDecimalValue(decimal value)
        {
            if (IsNullOrEmpty(value)) return 0;
            if (value != decimal.Truncate(value))
                return decimal.Parse(value.ToString().TrimEnd(DecimalSeparator, '0'));
            else
                return decimal.Truncate(value);

        }
        #endregion


        public static T CopyObjectValues<T>(T source, T destination) where T : class, IObjectWithChangeTracker
        {
            //FieldInfo[] fields = typeof(T).GetFields();
            //foreach (FieldInfo fi in fields)
            //{
            //    fi.SetValue(destination, fi.GetValue(source));
            //}

            T clone = null;

            var dcs = new System.Runtime.Serialization.DataContractSerializer(source.GetType());
            using (var ms = new System.IO.MemoryStream())
            {
                dcs.WriteObject(ms, source);
                ms.Seek(0, System.IO.SeekOrigin.Begin);
                clone = dcs.ReadObject(ms) as T;
            }


            clone.ChangeTracker.CleanUp(typeof(T).GetProperties().Select(p => p.Name).ToList());
            clone.StopTracking();

            //+RAB - 11.10.2010 - dont copy value for const and static elements
            foreach (FieldInfo fi in typeof(T).GetFields().Where(f => !f.Attributes.HasFlag(FieldAttributes.Literal)))
            {
                fi.SetValue(destination, fi.GetValue(clone));
            }
            //+RAB

            foreach (PropertyInfo prop in typeof(T).GetProperties())
            {
                //if (prop.PropertyType.Name == "TrackableCollection`1") 
                if (typeof(IList).IsAssignableFrom(prop.PropertyType))
                {
                    IList sourceCol = prop.GetValue(clone, null) as IList;
                    IList destCol = prop.GetValue(destination, null) as IList;

                    IObjectWithChangeTracker destColCT = destCol as IObjectWithChangeTracker;
                    if (destColCT != null)
                    {
                        destColCT.StopTracking();
                        destCol.Clear();
                        foreach (object x in sourceCol)
                        {
                            IObjectWithChangeTracker xct = x as IObjectWithChangeTracker;
                            if (xct != null) xct.StopTracking();
                            destCol.Add(x);
                            if (xct != null) xct.StartTracking();
                        }
                        destColCT.StartTracking();
                    }
                    else
                    {
                        ArrayList ar = new ArrayList();
                        ar.AddRange(sourceCol);
                        destCol.Clear();

                        foreach (object x in ar)
                        {
                            IObjectWithChangeTracker xct = x as IObjectWithChangeTracker;
                            if (xct != null) xct.StopTracking();
                            destCol.Add(x);
                            if (xct != null) xct.StartTracking();
                        }
                    }

                }
                else
                    if (typeof(IObjectWithChangeTracker).IsAssignableFrom(prop.PropertyType))
                    {
                        prop.SetValue(destination, null, null);
                        IObjectWithChangeTracker nv = prop.GetValue(clone, null) as IObjectWithChangeTracker;
                        // - 28.07.2010 - Test null value
                        if (nv != null)
                        {
                            nv.StopTracking();
                            prop.SetValue(clone, null, null);
                            prop.SetValue(destination, nv, null);
                            nv.StartTracking();
                        }
                        //
                    }
                    else
                        prop.SetValue(destination, prop.GetValue(clone, null), null);
            }




            return destination;
        }





        protected static void DisableChangeTracker(object x)
        {
            IObjectWithChangeTracker ct = x as IObjectWithChangeTracker;
            if (ct != null) ct.StopTracking();
        }

        /// <summary>
        /// Checks if the entity object is of type T
        /// If not, a new T instance is created and all entity of properties are copied
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static T ValidateObject<T>(T entity) where T : class, IObjectWithChangeTracker, new()
        {
            if (entity.GetType().Equals(typeof(T)))
                return entity;

            T res = new T();
            return CopyObjectValues<T>(entity, res);
            //FieldInfo[] fields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            //foreach (FieldInfo fi in fields)
            //{
            //    fi.SetValue(res, fi.GetValue(entity));
            //}
            //return res;
        }


        ///// <summary>
        ///// Copies values of an object to the destination one without copying Change Tracker object
        ///// </summary>
        ///// <typeparam name="T"></typeparam>
        ///// <param name="source"></param>
        ///// <param name="destination"></param>
        //public static void CopyObjectValuesWithoutChangeTracker<T>(T source, T destination) where T : class
        //{
        //    PropertyInfo[] propInfos = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        //    foreach (PropertyInfo pi in propInfos)
        //    {
        //        if (!pi.PropertyType.Name.Equals("ObjectChangeTracker"))
        //        {
        //            pi.GetSetMethod().Invoke(destination, new object[] { pi.GetGetMethod().Invoke(source, null) });
        //        }
        //    }
        //}



    }
}



